﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.WindowsMobile.DirectX.Direct3D;
using Microsoft.WindowsMobile.DirectX;

namespace PFD
{
    public class VertexBufferX : VertexBufferBase
    {
        
        /// <summary>
        /// Variable to store the actual Direct3D vertex buffer object
        /// </summary>
        VertexBuffer vertBuff;

        bool bVertBuffLocked = false;

        /// <summary>
        /// The function converts between the VertexElementType Type and the Direct3D
        /// known VertexFormats Type.
        /// </summary>
        /// <param name="vet"></param>
        /// <returns></returns>
        private VertexFormats ConvertVertexFormat(VertexElementType vet)
        {
            VertexFormats vf = 0;
            bContainsColors = false;
            bContainsNormals = false;
            bContainsTextureSet1 = false;
            bContainsTextureSet2 = false;
            ColorOffset = 0;
            NormalOffset = 0;
            TextureSet1Offset = 0;
            TextureSet2Offset = 0;

            if ((vet & VertexElementType.Position) == VertexElementType.Position)
            {
                vertex_stride = 3;
                vf |= VertexFormats.Position;
            }
            if ((vet & VertexElementType.Normal) == VertexElementType.Normal)
            {
                NormalOffset = vertex_stride;
                vertex_stride += 3;
                bContainsNormals = true;
                vf |= VertexFormats.Normal;
            }
            if ((vet & VertexElementType.Color) == VertexElementType.Color)
            {
                ColorOffset = vertex_stride;
                vertex_stride += 4;
                bContainsColors = true;
                vf |= VertexFormats.Diffuse;
            }
            if ((vet & VertexElementType.TextureSet1Coordinates) == VertexElementType.TextureSet1Coordinates)
            {
                TextureSet1Offset = vertex_stride;
                vertex_stride += 2;
                bContainsTextureSet1 = true;
                if ((vet & VertexElementType.TextureSet2Coordinates) == VertexElementType.TextureSet2Coordinates)
                {
                    TextureSet2Offset = vertex_stride;
                    vertex_stride +=2;
                    bContainsTextureSet2 = true;
                    vf |= VertexFormats.Texture2;
                }
                else
                {
                    vf |= VertexFormats.Texture1;
                }
            }
            else
            {
                if ((vet & VertexElementType.TextureSet2Coordinates) == VertexElementType.TextureSet2Coordinates)
                    throw new ArgumentException("Having Texture Set2 without Texture Set1 coordinates is not possible");
                else
                    vf |= VertexFormats.Texture0;
            }
            return vf;
        }

        /// <summary>
        /// Public constructor. Create a Direct3D vertex buffer object for the given number
        /// of vertices, the given Device using the provided vertex element type.
        /// </summary>
        /// <param name="numVerts"></param>
        /// <param name="vet"></param>
        /// <param name="d"></param>
        public VertexBufferX(uint numVerts, VertexElementType vet, Device d)
            : base(numVerts, vet)
        {
            array = new float[stride * NumberOfVertices];

            OnDeviceLost(d);
        }

        public void OnDeviceLost(Device d)
        {
            if (vertBuff != null)
            {
                vertBuff.Dispose();
                vertBuff = null;
            }

            Pool p;

            if (d.DeviceCaps.SurfaceCaps.SupportsManagedPool)
            {
                p = Pool.Managed;
            }
            else
            {
                if (d.DeviceCaps.SurfaceCaps.SupportsVidVertexBuffer)
                {
                    p = Pool.VideoMemory;
                }
                else
                {
                    p = Pool.SystemMemory;
                }
            }

            VertexFormats vf = ConvertVertexFormat(vertexElementType);
            int size = (int)stride * (int)NumberOfVertices * sizeof(float);
            vertBuff = new VertexBuffer(d, size, Usage.Lockable, vf, p);
        }

        /// <summary>
        /// Lock function provides access to the Direct3D vertex buffer object.
        /// The vertices from the buffer can be access through the base classes
        /// array member.
        /// </summary>
        protected override void Lock()
        {
            if (!bVertBuffLocked)
            {
                bVertBuffLocked = true;
            }
        }

        /// <summary>
        /// The function unlocks the vertex buffer object. the base class' member
        /// array may not be used to access the vertices anymore.
        /// </summary>
        protected override void Unlock()
        {
            if (bVertBuffLocked)
            {
                GraphicsStream str = vertBuff.Lock(0, 0, LockFlags.None);
                str.Write(array);
                vertBuff.Unlock();
                bVertBuffLocked = false;
            }
        }
        
        #region IVertexBuffer Members

        public override bool RealizeVertexBuffer(IRenderDevice rd, bool AllowSinglepassMultitexture, uint singlePassTextureNum)
        {
            Unlock();

            RenderDeviceX rx = (RenderDeviceX)rd;

            rx.GetDevice().SetStreamSource(0, vertBuff, 0);
            
            return true;
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// The function disposes this object and all related objects.
        /// </summary>
        public override void Dispose()
        {
            if (!vertBuff.Disposed)
                vertBuff.Dispose();
        }

        #endregion

        /// <summary>
        /// This function checks whether a Direct3D error occured and throws an exception if that
        /// was the case. Otherwise it will return false to indicate that no error occured.
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        protected override bool CheckError(string msg)
        {
            /// TODO: Check error implementation for DirectX Vertex Buffers
            /// 

            return false;
        }
    }
}
